﻿using Microsoft.Xna.Framework;
using Services;
using Utilities;

namespace EntitiesAndGameState
{

  /// <summary>
  /// Lo stato contiene la descrizione della scena e pochi servizi
  /// utili. Lo stato per ora é dato da:
  /// * nave del giocatore
  /// * navi nemiche
  /// * laser in volo (ogni laser ha una faction con cui sappiamo se
  ///   é del giocatore o del nemico)
  /// * missili nucleari in volo (hanno anche loro il campo faction)
  /// * timer di slow-motion
  /// * bonus correntemente attivi
  /// </summary>
  /// <remarks>
  /// !MOLTO IMPORTANTE!
  /// Non usiamo references da nessuna parte, per avere la massima velocitá
  /// di allocazione/deallocazione. Quindi manteniamo un pool di struct che
  /// sono i dati di ogni oggetto. Ship contiene solo un intero: l'indice della
  /// nave nel pool di navi memorizzate; le proprietá di Ship sono solo dei
  /// proxy per andare a modificare direttamente i dati della nave vera e
  /// propria (vedere definizione di Ship e ShipData).
  /// 
  /// I dati di un oggetto, ad esempio delle navi, sono mantenute dalla classe
  /// statica StructAllocator[ShipData]; tale classe ha i metodi New e Delete
  /// per riservarci (o liberare) un campo all'interno del pool.
  /// </remarks>
  public enum GameState
  {
    Defeat,
    Victory,
    None
  }

  public enum GameMode
  {
    Tutorial,
    Survival,
    None
  }

  public class State
  {

    public ArrayList<Ship> enemies;
    public ArrayList<ShipToAdd> enemies_to_add = new ArrayList<ShipToAdd>(30);
    public ArrayList<ShipToRemove> enemies_to_remove = new ArrayList<ShipToRemove>(30);

    public ArrayList<ShockWave> shockwaves = new ArrayList<ShockWave>(30);

    public ArrayList<Asteroid> asteroids;
    public ArrayList<GarbageSacToAdd> asteroids_to_add = new ArrayList<GarbageSacToAdd>(30);

        static public readonly float TRASH_UNLOADING_ANIMATION_TIME = 5;
        static public readonly float MAX_SLOW_MOTION_TIME = 10.0f;
        static public readonly float MAX_ALLERT_MOTION_TIME = 0.5f;
        static public readonly float MAX_BUZZ_MOTION_TIME = 0.5f;
        static public readonly float TRASH_BIN_CAPACITY = 7;
        static public readonly float MAN_HOLE_CAPACITY = 5;
        static public readonly float MAN_HOLE_MAX_TIME = 2;
        static public readonly float MAN_HOLE_STEP = MAN_HOLE_MAX_TIME / MAN_HOLE_CAPACITY;
        static public int MAX_GREEN_TRASH_BIN_CAPACITY = 15;
        static public int MAX_BLUE_TRASH_BIN_CAPACITY = 15;
        static public int MAX_YELLOW_TRASH_BIN_CAPACITY = 15;
        static public readonly float BUZZ_ERROR_DURATION = 0.10f;
        static public readonly float BUZZ_EARTH_DURATION = 0.75f;
        

    //static public readonly float TRASH_UNLOADING_ANIMATION_TIME = 5;
    //static public readonly float MAX_SLOW_MOTION_TIME = 10.0f;
    //static public readonly float MAX_ALLERT_MOTION_TIME = 0.5f;
    //static public readonly float MAX_BUZZ_MOTION_TIME = 0.5f;
    //static public readonly float TRASH_BIN_CAPACITY = 7;
    //static public readonly float MAN_HOLE_CAPACITY = 5;
    //static public readonly float MAN_HOLE_MAX_TIME = 2;
    //static public readonly float MAN_HOLE_STEP = MAN_HOLE_MAX_TIME / MAN_HOLE_CAPACITY;
    //static public readonly int MAX_GREEN_TRASH_BIN_CAPACITY = 3;
    //static public readonly int MAX_BLUE_TRASH_BIN_CAPACITY = 3;
    //static public readonly int MAX_YELLOW_TRASH_BIN_CAPACITY = 3;
    //static public readonly float BUZZ_ERROR_DURATION = 0.10f;
    //static public readonly float BUZZ_EARTH_DURATION = 0.75f;


    public const float TIME_FOR_LEVEL = 20.0f;

    static public readonly float MAX_NUM_OF_GARBAGE = 15;

    public int num_of_asteroids_hit = 0;
    public int num_of_bubble_wave = 0;
    public int EarthLife { get { return Background.EarthLife; } }
    public int GreenTrashBinState = 0;
    public int YellowTrashBinState = 0;
    public int BlueTrashBinState = 0;
    public int ManHoleState = 0;
    public int level = 1;
    public float totalgametime = 0;
    public float time_to_next_level = TIME_FOR_LEVEL;
    public bool lunch_shock_wave = false;

    public float yellow_trash_unloading_animation = 0;
    public float green_trash_unloading_animation = 0;
    public float blue_trash_unloading_animation = 0;

    public float ManHoleAnimationTimer = 0;
    public bool ManHoleActive = false;
    public bool is_man_hole_enable = true;
    public float smog_val = 0;
    public bool onTap = false;


    static public readonly float MAX_TIME_WAIT = 5;
    public float time_wait = MAX_TIME_WAIT;


    public BoundingSphere bounding_sphere = new BoundingSphere(Vector3.Zero, 5f);
    public ArrayList<X> xs = new ArrayList<X>(15);
    public bool IsZoomed = false;
    public GameState game_state = GameState.None;
    public GameMode game_mode = GameMode.None;

    public void RemoveShockWaveAt(int index)
    {
      var s = shockwaves[index];
      StructAllocator<ShockWaveData>.Delete(s.self);
      shockwaves.RemoveAt(index);
    }
    public void RemoveShipAt(int index)
    {
      var p = enemies[index];
      StructAllocator<ShipData>.Delete(p.self);
      enemies.RemoveAt(index);
    }


    public void RemoveAsteroidAt(int index)
    {
      var p = asteroids[index];
      StructAllocator<AsteroidData>.Delete(p.self);
      asteroids.RemoveAt(index);
    }

  }

  /// <summary>
  /// Questa interfaccia definisce un gioco come uno stato e
  /// delle esplosioni appena generate (per dare feedback sulle)
  /// collisioni "importanti". Potrebbe essere che ci aggiungiamo
  /// uno score, che ereditiamo State, che ereditiamo IStateProvider,
  /// etc.
  /// </summary>
  public interface IStateProvider<T>
  {
    State CurrentState { get; }
    ArrayList<Explosion> NewExplosions { get; }
    ArrayList<ShockWave> NewShockWaves { get; }
    bool Enabled { get; set; }
    T CurrentScore { get; }
  }
}
